/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.chemistry.opencmis.demo;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import org.alfresco.cmis.client.AlfrescoDocument;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectId;
import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Repository;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.Tree;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;

/**
 * 
 * This is the original hello world chemistry opencmis maven sample class
 * slightly changed/enriched with more samples of opencmis api usage including
 * Alfresco extension.
 * 
 * @author Rui Fernandes
 * 
 */
public class Demo {

	private static final boolean DO_NOT_DELETE = false;

	private static final String DEMO_CHARSET = "UTF-8";
	private static final String USER = "admin";
	private static final String PASSWORD = "admin";
	private static final String CMIS_REPO_URL = "http://localhost:8080/alfresco/s/cmis";
	private static final String TEST_FOLDER = "testFolder";
	private static final String DOC_NAME = "testFile.txt";
	private static final String DOC_NAME_2 = "testFile-2.txt";
	private static final String MIME_TYPE = "text/plain; charset=UTF-8";
	private static final String DEMO_CONTENT = "Just a simple test";

	/**
	 * 
	 * The main method executes different CMIS operations basically playing with
	 * one folder and one child document. Everything is cleaned at the end.
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		System.out.println(Demo.class.getName() + " started");

		CMISUtil cmisUtil = new CMISUtil(CMIS_REPO_URL, USER, PASSWORD, true);
		Repository repository = cmisUtil.getRepository();
		System.out.println("\nFound repository: " + repository.getName());

		Session session = cmisUtil.connect();
		System.out.println("Got a connection to repository: "
				+ repository.getName() + ", with id: " + repository.getId());

		Folder root = session.getRootFolder();

		// create folder in root
		Folder testFolder = createFolder(root);

		// Get everything in the root folder and print the names of the objects
		listObjects(root);

		// create document
		System.out.println("\nCreate simple document " + DOC_NAME);
		CmisObject docObject = createDocument(session, testFolder);

		// read document properties
		System.out.println("\nRead document property name: "
				+ docObject.getProperty("cmis:name").getValueAsString());

		// read content
		try {
			System.out.println("\nRead document content: \""
					+ getContentAsString(((Document) docObject)
							.getContentStream()) + "\"");
		} catch (IOException e) {
			e.printStackTrace();
		}

		// update document properties
		updateProperties(docObject);

		// check aspect
		System.out.println("\nDoes the document has the titled aspect? "
				+ ((AlfrescoDocument) docObject).hasAspect("P:cm:titled"));

		// check types
		checkTypes(session, docObject);

		// queries
		query(session);

		// access a folder directly
		readFolder(session, testFolder.getId());

		// exit
		if (DO_NOT_DELETE)
			System.exit(0);

		System.out.println("\nDeleting test folder and document...");
		docObject.delete(true);
		testFolder.delete(true);

		// Get everything in the root folder and print the names of the objects
		listObjects(root);

		System.out.println("\n" + Demo.class.getName() + " ended");
	}


	private static Folder createFolder(Folder root) {
		Map<String, String> property = new HashMap<String, String>();
		property.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
		property.put(PropertyIds.NAME, TEST_FOLDER);
		System.out.println("\nCreating test folder...");
		return root.createFolder(property);
	}

	private static void listObjects(Folder folder) {
		ItemIterable<CmisObject> children = folder.getChildren();
		System.out.println("\nFound the following objects in the "
				+ folder.getPath() + " folder:");
		int i = 1;
		for (CmisObject o : children) {
			System.out.println(i + "- " + o.getName());
			i++;
		}
	}

	private static void readFolder(Session session, String id) {
		System.out.println("\nReading test folder id=" + id);
		CmisObject cmisObject = session.getObject(session.createObjectId(id));
		Folder readTestFolder = (Folder) cmisObject;
		listObjects(readTestFolder);
	}

	private static CmisObject createDocument(Session session, Folder folder) {
		byte[] buf = null;
		try {
			buf = DEMO_CONTENT.getBytes(DEMO_CHARSET);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		ByteArrayInputStream input = new ByteArrayInputStream(buf);
		ContentStream contentStream = session.getObjectFactory()
				.createContentStream(DOC_NAME, buf.length, MIME_TYPE, input);
		Map<String, String> property = new HashMap<String, String>();
		// note we are adding an aspect
		property.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document,P:cm:titled");
		property.put(PropertyIds.NAME, DOC_NAME);
		ObjectId objectId = folder.createDocument(property, contentStream,
				VersioningState.MAJOR);
		System.out.println("Document created " + objectId.getId());
		return session.getObject(objectId);
	}

	private static void updateProperties(CmisObject docObject) {
		Map<String, String> property = new HashMap<String, String>();
		property.put("cmis:name", DOC_NAME_2);
		// note we are setting an aspect property
		property.put("cm:description", "My document");
		docObject.updateProperties(property);
	}

	private static void checkTypes(Session session, CmisObject cmisObject) {
		System.out.println("\nGetting type definition for doc type");
		ObjectType objectType = session.getTypeDefinition(cmisObject.getType()
				.getId());
		System.out.println("isBaseType() returns "
				+ (objectType.isBaseType() ? "true" : "false"));
		ObjectType baseType = objectType.getBaseType();
		if (baseType == null) {
			System.out.println("getBaseType() returns null");
		} else {
			System.out.println("getBaseType() returns "
					+ baseType.getDisplayName());
		}
		ObjectType parentType = objectType.getParentType();
		if (parentType == null) {
			System.out.println("getParentType() returns null");
		} else {
			System.out.println("getParentType() returns "
					+ parentType.getDisplayName());
		}
		System.out.println("Listing child types of "
				+ objectType.getDisplayName());
		for (ObjectType o : objectType.getChildren()) {
			System.out.println("\t" + o.getDisplayName());
		}
		System.out.println("Getting immediate descendant types of "
				+ objectType.getDisplayName());
		for (Tree<ObjectType> o : objectType.getDescendants(1)) {
			System.out.println("\t" + o.getItem().getDisplayName());
		}
	}

	private static void query(Session session) {
		System.out.print("\nQuery the repo");
		String query = "SELECT * FROM cmis:document WHERE cmis:name LIKE 'testFile%'";
		ItemIterable<QueryResult> queryResult = session.query(query, false);
		for (QueryResult item : queryResult) {
			System.out.print(" - property cmis:createdBy on "
					+ item.getPropertyByQueryName("cmis:name").getFirstValue()
					+ " is "
					+ item.getPropertyByQueryName("cmis:createdBy")
							.getFirstValue() + "\n");
		}
	}

	private static String getContentAsString(ContentStream stream)
			throws IOException {
		InputStream in2 = stream.getStream();
		StringBuffer sbuf = null;
		sbuf = new StringBuffer(in2.available());
		int count;
		byte[] buf2 = new byte[100];
		while ((count = in2.read(buf2)) != -1) {
			for (int i = 0; i < count; i++) {
				sbuf.append((char) buf2[i]);
			}
		}
		in2.close();
		return sbuf.toString();
	}

}
